/***
 * @brief IO协程调度器
 * @details
 * 1.注册事件  ->  2.等待事件  -> 3.事件触发回调  -> 注销事件回调后从epoll注销  -> 执行回调进入调度器执行调度
 */

// 所谓IO协程调度，其实就是在调度协程时，如果发现这个协程在等待IO就绪，那就先让这个协程让出执行权，等对应的IO就绪后再重新恢复这个协程的运行

#ifndef __IOMANAGER_H__
#define __IOMANAGER_H__

#include "scheduler.h"
#include "timer.h"

class IOManager : public Scheduler, public TimerManager
{
public:
    typedef std::shared_ptr<IOManager> ptr;
    typedef RWMutex RWMutexType;

    // IO事件,继承自epoll对事件的定义
    // 这里只关心socket fd的读写事件,其他epoll事件会归类到这两类事件中
    enum Event
    {
        // 无事件
        NONE = 0x0,
        // 读事件(EPOLLIN)
        READ = 0x1,
        // 写事件(EPOLLOUT)
        WRITE = 0x4,
    };

private:
    /**
     * @brief socket fd 上下文 用于描述一个文件描述符的事件上下文
     * @details 每个socket fd都对应一个FdContext,包括fd的值,fd上的事件,以及fd的读写事件上下文
     */
    struct FdContext
    {
        typedef Mutex MutexType;
        /***
         * @brief 事件上下文
         * @details fd的每一个事件都有一个事件上下文,保存这个事件的回调函数以及执行回调函数的调度器
         * 只做读事件和写事件,所有的事件都被归类到这两类事件中
         */
        struct EventContext
        {
            // 执行事件回调的调度器
            Scheduler *scheduler = nullptr;
            // 事件回调协程
            Fiber::ptr fiber;
            // 事件回调函数
            std::function<void()> callback;
        };

        // 获取事件的上下文
        EventContext &getEventContext(Event event);

        // 重置事件上下文
        void resetEventContext(EventContext &ctx);

        // 触发事件;根据事件类型调用对应上下文界面的调度器去调度协程或函数
        void triggerEvent(Event event);

        // 读事件上下文
        EventContext read;
        // 写事件上下文
        EventContext write;
        int fd;
        // 当前注册的事件
        Event events = NONE;
        MutexType mutex;
    };

public:
    IOManager(size_t threads = -1, bool use_caller = true, const std::string &name = "IOManager");
    ~IOManager();

    // 添加事件
    int addEvent(int fd, Event event, std::function<void()> cb = nullptr);

    // 删除事件
    bool delEvent(int fd, Event event);

    // 取消事件
    bool cancelEvent(int fd, Event event);

    // 取消所有事件
    bool cancelAll(int fd);

    // 返回当前的IOManager
    static IOManager *GetThis();

protected:
    // 通知调度器有任务要调度
    void tickle() override;

    // 判断是否可以停止
    bool stopping() override;

    // 对于IO协程调度来说,应阻塞在等待IO事件上,idle退出的时机是epoll_wait返回
    // 对应的操作是tickle或注册的IO事件发生
    void idle() override;

    // 判断是否可以停止,同时获取最近一个定时器的超时时间 timeout最近一个定时器的超时时间
    // 用于idle协程的epoll_wait
    bool stopping(uint64_t &timeout);

    // 当有定时器插入到头部的时候,要重新更新epoll_wait的超时时间,这里唤醒idle协程以便于使用新的超时时间
    void onTimerInsertAtFront() override;

    // 重置socket句柄上下文的容器大小
    void contextResize(size_t size);

private:
    // epoll文件描述符
    int m_epfd = 0;
    // pipe 文件句柄,fd[0]读端,fd[1]写端
    int m_tickleFds[2]; // 用户线程间通信的管道文件描述符
    // 当前等待执行的IO事件数量
    std::atomic<size_t> m_pendingEventCount = {0};
    // 读写锁
    RWMutexType m_mutex;
    // socket 事件上下文的容器
    std::vector<FdContext *> m_fdContexts;
};

#endif